<template>
  <div ref="container" class="scene-container"></div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import * as dat from "dat.gui";
/**
 * 聚光灯
 */
// 给当前组件定义名称
defineOptions({
  name: "ThreeScene",
});

const container = ref<HTMLElement>();
let scene: THREE.Scene;
let camera: THREE.PerspectiveCamera;
let renderer: THREE.WebGLRenderer;
let controls: OrbitControls;
let sphere: THREE.Mesh;
let plane: THREE.Mesh;
let spotLight: THREE.SpotLight;

// 初始化场景
const initScene = () => {
  // 创建场景
  scene = new THREE.Scene();

  // 创建相机
  camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );
  camera.position.set(0, 5, 10);

  // 创建渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  container.value?.appendChild(renderer.domElement);

  // 创建球体
  const sphereGeometry = new THREE.SphereGeometry(1, 32, 32);
  const sphereMaterial = new THREE.MeshStandardMaterial({
    color: 0xffffff,
    roughness: 0.3, // 粗糙度
    metalness: 0.2, // 金属度
  });
  sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
  sphere.position.set(0, 1, 0);
  sphere.castShadow = true;
  scene.add(sphere);

  // 创建平面
  const planeGeometry = new THREE.PlaneGeometry(20, 20);
  const planeMaterial = new THREE.MeshStandardMaterial({
    color: 0xffffff,
    roughness: 0.4, // 粗糙度
    metalness: 0, // 金属度
  });
  plane = new THREE.Mesh(planeGeometry, planeMaterial);
  plane.rotation.x = -Math.PI / 2;
  plane.position.y = 0;
  plane.receiveShadow = true;
  scene.add(plane);

  // 创建聚光灯
  spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(1, 5, 0);
  spotLight.target = sphere;
  spotLight.angle = Math.PI / 8;
  spotLight.penumbra = 0.1;
  spotLight.decay = 1; // 沿光照距离的衰减量
  spotLight.distance = 30;
  spotLight.intensity = 2;

  // 设置阴影参数
  spotLight.castShadow = true;
  spotLight.shadow.mapSize.width = 2048;
  spotLight.shadow.mapSize.height = 2048;
  spotLight.shadow.camera.near = 0.5;
  spotLight.shadow.camera.far = 30;
  spotLight.shadow.focus = 1;

  scene.add(spotLight);

  // 添加环境光
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.1);
  scene.add(ambientLight);

  // 添加坐标轴辅助
  const axesHelper = new THREE.AxesHelper(5);
  scene.add(axesHelper);

  // 添加聚光灯辅助
  const spotLightHelper = new THREE.SpotLightHelper(spotLight);
  scene.add(spotLightHelper);

  // 添加GUI控制
  const gui = new dat.GUI();
  const spotLightFolder = gui.addFolder("SpotLight");
  spotLightFolder
    .add(spotLight, "angle", 0, Math.PI / 2)
    .name("角度")
    .onChange(() => {
      spotLightHelper.update();
    });
  spotLightFolder.add(spotLight, "decay", 0, 10).name("沿光照距离的衰减量");
  spotLightFolder.add(spotLight, "penumbra", 0, 1).name("边缘软化");
  spotLightFolder.add(spotLight, "intensity", 0, 5).name("强度");
  spotLightFolder
    .add(spotLight.position, "y", 0, 20)
    .name("高度")
    .onChange(() => {
      spotLightHelper.update();
    });

  // 添加轨道控制器
  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  controls.dampingFactor = 0.05;

  // 开始动画循环
  animate();
};

// 动画循环函数
const animate = () => {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
};

// 处理窗口大小变化
const handleResize = () => {
  if (camera && renderer) {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  }
};

// 组件挂载时初始化
onMounted(() => {
  initScene();
  window.addEventListener("resize", handleResize);
});

// 组件卸载时清理
onBeforeUnmount(() => {
  window.removeEventListener("resize", handleResize);
});
</script>

<style scoped>
.scene-container {
  width: 100%;
  height: 100vh;
  overflow: hidden;
}
</style>
